﻿using System;
using System.Collections.Generic;
using System.Linq;
using Tierless.Framework.Data;
using Tierless.Framework.EntityTypes;

namespace Tierless.Framework.Client
{
	public class EntityTypeProvider : IEntityTypeProvider
	{
		public EntityTypeProvider()
		{
			EntityTypeCache = new Dictionary<string,EntityType>();
			if (EntityTypeRepository.EntityTypeProvider != null)
				throw new Exception("An entity type provider is already registered with the entity type repository.  Only once instance is allowed.");
			EntityTypeRepository.EntityTypeProvider = this;
		}

		public Dictionary<string, EntityType> EntityTypeCache { get; private set; }
		public object EntityTypeCacheLock = new Object();

		public virtual EntityType GetEntityType(string entityTypeName)
		{
			EntityType result;
			lock (EntityTypeCacheLock)
				if (EntityTypeCache.TryGetValue(entityTypeName, out result))
					return result;

			// Cache miss, retrieve the entity type
			return LoadEntityType(entityTypeName);
		}

		protected virtual EntityType LoadEntityType(string entityTypeName)
		{
			var client = new EntityClient();
			var result = client.Describe(new[] { entityTypeName });
			lock (EntityTypeCacheLock)
			{
				if (!EntityTypeCache.ContainsKey(entityTypeName))	// (Might have been added concurrently)
					EntityTypeCache.Add(entityTypeName, result[0]);
			}
			return result[0];
		}

		public virtual void EnsureEntityTypes(IEnumerable<string> entityTypeNames)
		{
			string[] toFetch;
			lock (EntityTypeCacheLock)
				toFetch = (from n in entityTypeNames where !EntityTypeCache.ContainsKey(n) select n).ToArray();

			if (toFetch.Length > 0)
			{
				var service = new EntityService();
				var types = service.Describe(toFetch);
				lock (EntityTypeCacheLock)
				{
					foreach (var type in types)
						if (!EntityTypeCache.ContainsKey(type.Name))	// (Might have been added concurrently)
							EntityTypeCache.Add(type.Name, type);
				}
			}
		}
	}
}
